package info.wangyuan.agent.utils;

import cn.hutool.core.collection.CollUtil;
import dev.langchain4j.data.document.Document;
import info.wangyuan.agent.common.Constant.EmbeddingConstant;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 文档元数据管理工具
 * 直接操作 Document 对象的元数据
 *
 * @author Albert
 * @since 2025-08-16
 */
@Slf4j
public class DocumentMetaUtils {

    /**
     * 元数据键常量
     */
    private static final String FILE_PATH = "file_path";
    private static final String FILE_NAME = "file_name";
    private static final String ABSOLUTE_DIRECTORY_PATH = "absolute_directory_path";

    /**
     * 私有构造函数，防止实例化
     */
    private DocumentMetaUtils() {
    }

    /**
     * 确保文档的文件路径
     *
     * @param doc 文档对象
     * @return 文件路径
     */
    private static String ensureFilePath(Document doc) {
        String filePath = doc.metadata().getString(FILE_PATH);
        if (filePath == null) {
            String dir = doc.metadata().getString(ABSOLUTE_DIRECTORY_PATH);
            String name = doc.metadata().getString(FILE_NAME);
            if (dir != null && name != null) {
                filePath = dir + File.separator + name;
                doc.metadata().put(FILE_PATH, filePath);
                log.warn("⚠️ 自动补全 {}: {}", FILE_PATH, filePath);
            } else {
                log.error("❌ 无法补全 {}: {}", FILE_PATH, doc);
                return null;
            }
        }
        return filePath;
    }

    /**
     * 为文档增强元数据
     *
     * @param docs     文档列表
     * @param rootPath 根路径
     */
    public static void enhanceDocuments(List<Document> docs, String rootPath) {
        if (CollUtil.isEmpty(docs)) {
            log.warn("文档列表为空，未执行元数据增强。");
            return;
        }
        for (Document doc : docs) {
            String filePath = ensureFilePath(doc);
            if (filePath == null) continue;

            File file = new File(filePath);
            doc.metadata().put(EmbeddingConstant.DOC_META_DATA_KB, extractKnowledgeBaseName(file, rootPath));
            doc.metadata().put(EmbeddingConstant.DOC_META_DATA_FN, file.getName());
        }
    }

    /**
     * 提取知识库名称
     *
     * @param file     文件对象
     * @param rootPath 根路径
     * @return 知识库名称
     */
    private static String extractKnowledgeBaseName(File file, String rootPath) {
        try {
            String canonicalRoot = new File(rootPath).getCanonicalPath();
            File parent = file.getParentFile();
            while (parent != null) {
                if (Objects.equals(parent.getCanonicalPath(), canonicalRoot)) return EmbeddingConstant.DEFAULT_DOC_META_DATA_KB;
                if (Objects.equals(parent.getParentFile().getCanonicalPath(), canonicalRoot)) return parent.getName();
                parent = parent.getParentFile();
            }
        } catch (Exception e) {
            log.error("提取知识库名失败 [{}]: {}", file.getAbsolutePath(), e.getMessage());
        }
        return EmbeddingConstant.DEFAULT_DOC_META_DATA_KB;
    }

    /**
     * 向文档元数据添加单个键值对
     *
     * @param doc   文档对象
     * @param key   键
     * @param value 值
     * @return 是否成功添加
     */
    public static boolean addMetadata(Document doc, String key, Object value) {
        if (key == null || value == null) return false;
        if (doc.metadata().containsKey(key)) {
            log.warn("键 {} 已存在，无法添加。", key);
            return false;
        }
        doc.metadata().put(key, (String) value);
        return true;
    }

    /**
     * 批量向文档元数据添加键值对
     *
     * @param doc  文档对象
     * @param meta 键值对集合
     * @return 是否成功添加
     */
    public static boolean addMetadata(Document doc, Map<String, Object> meta) {
        if (meta == null || meta.isEmpty()) return false;
        boolean changed = false;
        for (Map.Entry<String, Object> entry : meta.entrySet()) {
            if (addMetadata(doc, entry.getKey(), entry.getValue())) {
                changed = true;
            }
        }
        return changed;
    }

    /**
     * 更新文档元数据中的键值对
     *
     * @param doc   文档对象
     * @param key   键
     * @param value 新值
     * @return 是否成功更新
     */
    public static boolean updateMetadata(Document doc, String key, Object value) {
        if (key == null || value == null) return false;
        if (!doc.metadata().containsKey(key)) {
            log.warn("键 {} 不存在，无法更新。", key);
            return false;
        }
        doc.metadata().put(key, (String) value);
        return true;
    }

    /**
     * 删除文档元数据中的键值对
     *
     * @param doc 文档对象
     * @param key 键
     * @return 是否成功删除
     */
    public static boolean deleteMetadata(Document doc, String key) {
        if (key == null) return false;
        if (!doc.metadata().containsKey(key)) {
            log.warn("键 {} 不存在，无法删除。", key);
            return false;
        }
        doc.metadata().remove(key);
        return true;
    }

    /**
     * 获取文档元数据
     *
     * @param doc 文档对象
     * @return 文档元数据
     */
    public static Map<String, Object> getMetadata(Document doc) {
        return new HashMap<>(doc.metadata().toMap());
    }
}
